1 package edu.jiangxin.apktoolbox.android.dumpsys;
2
3 import edu.jiangxin.apktoolbox.android.dumpsys.tojson.IDumpsys2Json;
4 import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
5 import edu.jiangxin.apktoolbox.swing.extend.filepanel.FilePanel;
6 import edu.jiangxin.apktoolbox.utils.Constants;
7 import org.apache.commons.exec.CommandLine;
8 import org.apache.commons.exec.DefaultExecutor;
9 import org.apache.commons.exec.PumpStreamHandler;
10 import org.apache.commons.io.FileUtils;
11 import org.apache.commons.io.FilenameUtils;
12 import org.apache.commons.lang3.StringUtils;
13 import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
14 import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
15 import org.fife.ui.rtextarea.RTextScrollPane;
16
17 import javax.swing.*;
18 import java.awt.*;
19 import java.awt.event.ActionEvent;
20 import java.awt.event.ActionListener;
21 import java.io.ByteArrayOutputStream;
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.Serial;
25 import java.text.SimpleDateFormat;
26 import java.util.Date;
27
28 public class DumpsysPanel extends EasyPanel {
29 @Serial
30 private static final long serialVersionUID = 1L;
31
32 private JPanel dumpsysPanel;
33
34 private FilePanel dumpsysTargetDirPanel;
35
36 private JPanel dumpsysOptionPanel;
37 private JPanel dumpsysTypeChoosePanel;
38
39 private JPanel dumpsysOperationPanel;
40 private JButton dumpsysStartButton;
41
42 private JPanel analysisPanel;
43 private FilePanel analysisFilePanel;
44 private RSyntaxTextArea analysisOutputTextArea;
45 private RTextScrollPane analysisOutputScrollPane;
46
47 private JPanel analysisOperationPanel;
48
49 public DumpsysPanel() {
50 super();
51 }
52
53 @Override
54 public void initUI() {
55 setPreferredSize(new Dimension(700, 400));
56
57 JTabbedPane tabbedPane = new JTabbedPane();
58 add(tabbedPane);
59
60 createDumpsysPanel();
61 tabbedPane.addTab("Dumpsys", null, dumpsysPanel, "Dumpsys");
62
63 createAnalysisPanel();
64 tabbedPane.addTab("Analysis", null, analysisPanel, "Analysis");
65 }
66
67 @Override
68 public void afterPainted() {
69 super.afterPainted();
70 dumpsysTargetDirPanel.setPersistentKey(Constants.KEY_PREFIX + "dumpsysTargetDirPanel");
71 analysisFilePanel.setPersistentKey(Constants.KEY_PREFIX + "dumpsysAnalysisFilePanel");
72 }
73
74 private void createDumpsysPanel() {
75 dumpsysPanel = new JPanel();
76 BoxLayout layout = new BoxLayout(dumpsysPanel, BoxLayout.Y_AXIS);
77 dumpsysPanel.setLayout(layout);
78 dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
79
80 createDumpsysTargetDirPanel();
81 dumpsysPanel.add(dumpsysTargetDirPanel);
82 dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
83
84 createDumpsysOptionPanel();
85 dumpsysPanel.add(dumpsysOptionPanel);
86 dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
87
88 createDumpsysOperationPanel();
89 dumpsysPanel.add(dumpsysOperationPanel);
90 }
91
92 private void createAnalysisPanel() {
93 analysisPanel = new JPanel();
94 BoxLayout layout = new BoxLayout(analysisPanel, BoxLayout.Y_AXIS);
95 analysisPanel.setLayout(layout);
96 dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
97
98 createAnalysisTargetFilePanel();
99 analysisPanel.add(analysisFilePanel);
100 analysisPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
101
102 createAnalysisOutputPanel();
103 analysisPanel.add(analysisOutputScrollPane);
104 analysisPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
105
106 createAnalysisOperationPanel();
107 analysisPanel.add(analysisOperationPanel);
108 }
109
110 private void createAnalysisTargetFilePanel() {
111 analysisFilePanel = new FilePanel("Analysis File");
112 analysisFilePanel.initialize();
113 analysisFilePanel.setFileSelectionMode(JFileChooser.FILES_ONLY);
114 }
115
116 private void createAnalysisOutputPanel() {
117 analysisOutputTextArea = new RSyntaxTextArea();
118 analysisOutputTextArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JSON);
119 analysisOutputTextArea.setCodeFoldingEnabled(true);
120 analysisOutputTextArea.setEditable(false);
121
122 analysisOutputScrollPane = new RTextScrollPane(analysisOutputTextArea);
123 analysisOutputScrollPane.setPreferredSize(new Dimension(400, 250));
124 }
125
126 private void createAnalysisOperationPanel() {
127 analysisOperationPanel = new JPanel();
128 BoxLayout boxLayout = new BoxLayout(analysisOperationPanel, BoxLayout.X_AXIS);
129 analysisOperationPanel.setLayout(boxLayout);
130
131 JButton analysisStartButton = new JButton("Start");
132 analysisStartButton.setEnabled(true);
133 analysisStartButton.addActionListener(new AnalysisStartButtonActionListener());
134 analysisOperationPanel.add(analysisStartButton);
135 }
136
137 private void createDumpsysTargetDirPanel() {
138 dumpsysTargetDirPanel = new FilePanel("Target Directory");
139 dumpsysTargetDirPanel.initialize();
140 dumpsysTargetDirPanel.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
141 }
142
143 private void createDumpsysOptionPanel() {
144 dumpsysOptionPanel = new JPanel();
145 BoxLayout boxLayout = new BoxLayout(dumpsysOptionPanel, BoxLayout.Y_AXIS);
146 dumpsysOptionPanel.setLayout(boxLayout);
147
148 JPanel dumpsysCheckboxPanel = new JPanel();
149 dumpsysCheckboxPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 3));
150
151 JCheckBox allSelectedCheckBox = new JCheckBox("Select All");
152 allSelectedCheckBox.setSelected(false);
153 allSelectedCheckBox.addActionListener(e -> {
154 boolean selected = allSelectedCheckBox.isSelected();
155 if (selected) {
156 Component[] components = dumpsysTypeChoosePanel.getComponents();
157 for (Component component : components) {
158 if (component instanceof JCheckBox checkBox) {
159 checkBox.setSelected(true);
160 checkBox.setEnabled(false);
161 }
162 }
163 } else {
164 Component[] components = dumpsysTypeChoosePanel.getComponents();
165 for (Component component : components) {
166 if (component instanceof JCheckBox checkBox) {
167 checkBox.setSelected(false);
168 checkBox.setEnabled(true);
169 }
170 }
171 }
172
173 });
174 dumpsysCheckboxPanel.add(allSelectedCheckBox);
175
176 dumpsysTypeChoosePanel = new JPanel();
177 dumpsysTypeChoosePanel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 3));
178 dumpsysTypeChoosePanel.setPreferredSize(new Dimension(500, 200));
179
180 JCheckBox inputCheckBox = new JCheckBox("input");
181 inputCheckBox.setSelected(true);
182 JCheckBox windowCheckBox = new JCheckBox("window");
183 dumpsysTypeChoosePanel.add(windowCheckBox);
184 JCheckBox SurfaceFlingerCheckBox = new JCheckBox("SurfaceFlinger");
185 dumpsysTypeChoosePanel.add(SurfaceFlingerCheckBox);
186 JCheckBox activityCheckBox = new JCheckBox("activity");
187 dumpsysTypeChoosePanel.add(activityCheckBox);
188 JCheckBox accessibilityCheckBox = new JCheckBox("accessibility");
189 dumpsysTypeChoosePanel.add(accessibilityCheckBox);
190 JCheckBox packageCheckBox = new JCheckBox("package");
191 dumpsysTypeChoosePanel.add(packageCheckBox);
192 JCheckBox alarmCheckBox = new JCheckBox("alarm");
193 dumpsysTypeChoosePanel.add(alarmCheckBox);
194 JCheckBox inputMethodCheckBox = new JCheckBox("input_method");
195 dumpsysTypeChoosePanel.add(inputMethodCheckBox);
196 JCheckBox sensorServiceCheckBox = new JCheckBox("sensorservice");
197 dumpsysTypeChoosePanel.add(sensorServiceCheckBox);
198 JCheckBox accountCheckBox = new JCheckBox("account");
199 dumpsysTypeChoosePanel.add(accountCheckBox);
200 JCheckBox displayCheckBox = new JCheckBox("display");
201 dumpsysTypeChoosePanel.add(displayCheckBox);
202 JCheckBox powerCheckBox = new JCheckBox("power");
203 dumpsysTypeChoosePanel.add(powerCheckBox);
204 JCheckBox batteryCheckBox = new JCheckBox("battery");
205 dumpsysTypeChoosePanel.add(batteryCheckBox);
206
207 dumpsysOptionPanel.add(dumpsysCheckboxPanel);
208 dumpsysOptionPanel.add(new JSeparator(JSeparator.HORIZONTAL));
209 dumpsysOptionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
210 dumpsysOptionPanel.add(dumpsysTypeChoosePanel);
211 }
212
213 private void createDumpsysOperationPanel() {
214 dumpsysOperationPanel = new JPanel();
215 BoxLayout boxLayout = new BoxLayout(dumpsysOperationPanel, BoxLayout.X_AXIS);
216 dumpsysOperationPanel.setLayout(boxLayout);
217
218 dumpsysStartButton = new JButton("Start");
219 dumpsysStartButton.setEnabled(true);
220 dumpsysStartButton.addActionListener(new DumpsysStartButtonActionListener());
221 dumpsysOperationPanel.add(dumpsysStartButton);
222 }
223
224 class DumpsysStartButtonActionListener implements ActionListener {
225 @Override
226 public void actionPerformed(ActionEvent e) {
227 dumpsysStartButton.setEnabled(false);
228 File targetDirFile = dumpsysTargetDirPanel.getFile();
229 if (!targetDirFile.exists()) {
230 JOptionPane.showMessageDialog(getFrame(), "Target directory does not exist!", "ERROR", JOptionPane.ERROR_MESSAGE);
231 return;
232 }
233
234 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date(System.currentTimeMillis()));
235 File targetDirWithTimestamp = new File(targetDirFile, timeStamp);
236
237 if (!targetDirWithTimestamp.exists()) {
238 boolean success = targetDirWithTimestamp.mkdirs();
239 if (!success) {
240 JOptionPane.showMessageDialog(getFrame(), "Create target directory failed!", "ERROR", JOptionPane.ERROR_MESSAGE);
241 return;
242 }
243 }
244
245 Component[] components = dumpsysTypeChoosePanel.getComponents();
246 for (Component component : components) {
247 if (component instanceof JCheckBox checkBox) {
248 String dumpsysType = checkBox.getText();
249 if (checkBox.isSelected()) {
250 dumpsysAndSaveToFile(dumpsysType, targetDirWithTimestamp);
251 }
252 }
253 }
254 dumpsysStartButton.setEnabled(true);
255 }
256
257 private void dumpsysAndSaveToFile(String dumpsysType, File targetDir) {
258 String command = "adb shell dumpsys " + dumpsysType;
259 CommandLine cmdLine = CommandLine.parse(command);
260 DefaultExecutor executor = DefaultExecutor.builder().get();
261 try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
262 ByteArrayOutputStream errorStream = new ByteArrayOutputStream();) {
263 PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream, errorStream);
264 executor.setStreamHandler(streamHandler);
265 int exitValue = executor.execute(cmdLine);
266 if (exitValue == 0) {
267 String result = outputStream.toString().trim();
268 if (StringUtils.isNotEmpty(result)) {
269 File outputFile = new File(targetDir, dumpsysType + ".dumpsys");
270 FileUtils.writeStringToFile(outputFile, result, "UTF-8");
271 }
272 } else {
273 String errorResult = errorStream.toString().trim();
274 if (StringUtils.isNotEmpty(errorResult)) {
275 File errorFile = new File(targetDir, dumpsysType + ".error");
276 FileUtils.writeStringToFile(errorFile, errorResult, "UTF-8");
277 }
278 }
279 } catch (IOException ex) {
280 JOptionPane.showMessageDialog(getFrame(), "Execute command failed:\n" + ex.getMessage(), "ERROR", JOptionPane.ERROR_MESSAGE);
281 }
282 }
283 }
284
285 class AnalysisStartButtonActionListener implements ActionListener {
286
287 @Override
288 public void actionPerformed(ActionEvent e) {
289 File analysisFile = analysisFilePanel.getFile();
290 if (!analysisFile.exists()) {
291 JOptionPane.showMessageDialog(getFrame(), "Analysis file does not exist!", "ERROR", JOptionPane.ERROR_MESSAGE);
292 return;
293 }
294 String fileNameWithoutExtension = FilenameUtils.getBaseName(analysisFile.getName());
295 String className = StringUtils.capitalize(fileNameWithoutExtension) + "2Json";
296 IDumpsys2Json dumpsys2Json = getDumpsys2JsonInstance(className);
297 if (dumpsys2Json != null) {
298 String jsonResult = dumpsys2Json.dumpsys2Json(analysisFile);
299 analysisOutputTextArea.setText(jsonResult);
300 analysisOutputTextArea.setCaretPosition(0);
301 }
302 }
303
304 private IDumpsys2Json getDumpsys2JsonInstance(String className) {
305 try {
306 String fullClassName = "edu.jiangxin.apktoolbox.android.dumpsys.tojson." + className;
307 Class<?> clazz = Class.forName(fullClassName);
308 if (IDumpsys2Json.class.isAssignableFrom(clazz)) {
309 return (IDumpsys2Json) clazz.getDeclaredConstructor().newInstance();
310 } else {
311 JOptionPane.showMessageDialog(getFrame(), "Class " + fullClassName + " does not implement IDumpsys2Json!", "ERROR", JOptionPane.ERROR_MESSAGE);
312 return null;
313 }
314 } catch (ClassNotFoundException e) {
315 JOptionPane.showMessageDialog(getFrame(), "Class not found: " + className, "ERROR", JOptionPane.ERROR_MESSAGE);
316 return null;
317 } catch (Exception e) {
318 JOptionPane.showMessageDialog(getFrame(), "Error loading class: " + e.getMessage(), "ERROR", JOptionPane.ERROR_MESSAGE);
319 return null;
320 }
321 }
322 }
323 }